home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / quarkpy / maputils.py < prev    next >
Text File  |  2004-01-05  |  14KB  |  448 lines

  1. """   QuArK  -  Quake Army Knife
  2.  
  3. Various Map editor utilities.
  4. """
  5. #
  6. # Copyright (C) 1996-99 Armin Rigo
  7. # THIS FILE IS PROTECTED BY THE GNU GENERAL PUBLIC LICENCE
  8. # FOUND IN FILE "COPYING.TXT"
  9. #
  10.  
  11. #$Header: /cvsroot/quark/runtime/quarkpy/maputils.py,v 1.22 2003/12/18 21:51:46 peter-b Exp $
  12.  
  13.  
  14. import quarkx
  15. from qeditor import *
  16. from qdictionnary import Strings
  17.  
  18.  
  19.  
  20. #
  21. # Function below removed. Use "view.scale()" instead.
  22. #
  23.  
  24. #def scaleofview(view):
  25. #    "The scale of the given view, or 1.0 for 3D views."
  26. #    if view.info["type"]!="3D":
  27. #        try:
  28. #            return view.info["scale"]
  29. #        except KeyError:
  30. #            pass
  31. #    return 1.0
  32.  
  33.  
  34.  
  35. #
  36. # Is a given object still in the tree view, or was it removed ?
  37. #
  38. def checktree(root, obj):
  39.     while obj is not root:
  40.         t = obj.parent
  41.         if t is None or not (obj in t.subitems):
  42.             return 0
  43.         obj = t
  44.     return 1     
  45.  
  46.  
  47. #
  48. # The UserDataPanel class, overridden to be map-specific.
  49. #
  50.  
  51. def chooselocaltexture(item):
  52.     editor = mapeditor()
  53.     if editor is None: return
  54.     import mapbtns
  55.     mapbtns.applytexture(editor, item.text)
  56.  
  57. def loadlocaltextures(item):
  58.     editor = mapeditor()
  59.     if editor is None: return
  60.     items = []
  61.     for tex in quarkx.texturesof([editor.Root]):
  62.         m = qmenu.item(tex, chooselocaltexture)
  63.         m.menuicon = ico_objects[1][iiTexture]
  64.         items.append(m)
  65.     return items
  66.  
  67.  
  68. class MapUserDataPanel(UserDataPanel):
  69.  
  70.     def btnclick(self, btn):
  71.         #
  72.         # Send the click message to the module mapbtns.
  73.         #
  74.         import mapbtns
  75.         mapbtns.mapbuttonclick(btn)
  76.  
  77.     def buildbuttons(self, btnpanel):
  78.         Btns = []
  79.         ico_maped=ico_dict['ico_maped']
  80.         for tb, icon in (("New map items...", 25), ("Texture Browser...", 26)):
  81.             icons =  (ico_maped[0][icon], ico_maped[1][icon])
  82.             toolboxes = quarkx.findtoolboxes(tb)
  83.             for toolbox, root in toolboxes:
  84.                 new = quarkx.newobj(root.shortname + '.qtxfolder')
  85.                 new.appenditem(root.copy())
  86.                 btn = self.newbutton(new, btnpanel, icons)
  87.                 btn.toolbox = toolbox
  88.                 del btn.ondrop
  89.                 Btns.append(btn)
  90.             Btns.append(qtoolbar.smallgap)
  91.         new = quarkx.newobj(Strings[185]+".qtxfolder")
  92.         new.appenditem(quarkx.newobj("local:"))
  93.         btn = self.newbutton(new, btnpanel, icons)
  94.         btn.toolbox = Strings[185]
  95.         del btn.ondrop
  96.         del btn.onclick
  97.         btn.menu = loadlocaltextures
  98.         Btns.append(btn)
  99.         Btns.append(qtoolbar.newline)
  100.         return Btns + UserDataPanel.buildbuttons(self, btnpanel)
  101.  
  102.     def deletebutton(self, btn):
  103.         if hasattr(btn, "toolbox"):
  104.             quarkx.msgbox(Strings[5670] % btn.toolbox, MT_ERROR, MB_OK)
  105.         else:
  106.             UserDataPanel.deletebutton(self, btn)
  107.  
  108.     def drop(self, btnpanel, list, i, source):
  109.         if len(list)==1 and list[0].type == ':g':
  110.             quarkx.clickform = btnpanel.owner
  111.             editor = mapeditor()
  112.             if editor is not None and source is editor.layout.explorer:
  113.                 choice = quarkx.msgbox("You are about to create a new button from this group. Do you want the button to display a menu with the items in this group ?\n\nYES: you can pick up individual items when you click on this button.\nNO: you can insert the whole group in your map by clicking on this button.",
  114.                   MT_CONFIRMATION, MB_YES_NO_CANCEL)
  115.                 if choice == MR_CANCEL:
  116.                     return
  117.                 if choice == MR_YES:
  118.                     list = [group2folder(list[0])]
  119.  
  120.         if list[0].type=='.qtxfolder':
  121.             for item in list[0].subitems:
  122.                 item["fixedscale"]="1"
  123.         else:
  124.             for item in list:
  125.                 item["fixedscale"]="1"
  126.  
  127.         UserDataPanel.drop(self, btnpanel, list, i, source)
  128.  
  129.  
  130. def group2folder(group):
  131.     new = quarkx.newobj(group.shortname + '.qtxfolder')
  132.     for obj in group.subitems:
  133.         if obj.type == ':g':
  134.             obj = group2folder(obj)
  135.         else:
  136.             obj = obj.copy()
  137.         new.appenditem(obj)
  138.     return new
  139.  
  140. def degcycle(shear):
  141.     if shear > 180:
  142.       shear = shear-360
  143.     if shear < -180:
  144.       shear = shear+360
  145.     return shear
  146.  
  147. def undo_exchange(editor, old, new, msg):
  148.   undo = quarkx.action()
  149.   undo.exchange(old, new)
  150.   editor.ok(undo, msg)
  151.  
  152. def perptonormthru(source, dest, normthru):
  153.   "the line from source to dest that is perpendicular to (normalized) normthru"
  154.   diff = source-dest
  155.   dot = diff*normthru
  156.   return diff - dot*normthru
  157.   
  158. #
  159. # Sets sign of vector so that its dot product is
  160. #  positive w.r.t. the axis it's most closely
  161. #  colinear with
  162. #
  163. def set_sign(vec):
  164.   gap = ind = 0
  165.   tuple = vec.tuple
  166.   for i in range(3):
  167.     if tuple[i]>gap:
  168.       gap = tuple[i]
  169.       ind = i
  170.     if gap < 0:
  171.       return -vec
  172.     else:
  173.       return vec
  174. def ArbRotationMatrix(normal, angle):
  175.      # qhandles.UserRotationMatrix with an angle added
  176.      # normal: normal vector for the view plane
  177.      # texpdest: new position of the reference vector texp4
  178.      # texp4: reference vector (handle position minus rotation center)
  179.      # g1: if True, snap angle to grid
  180.     SNAP = 0.998
  181.     cosangle = math.cos(angle)
  182.     sinangle = math.sin(angle)
  183. #    oldcos = cosangle
  184. #    cosangle = cosangle*cosa-sinangle*sina
  185. #    sinangle = sinangle*cosa+sina*oldcos
  186.  
  187.     m = quarkx.matrix((cosangle,  sinangle, 0),
  188.                       (-sinangle, cosangle, 0),
  189.                       (    0,        0,     1))
  190.     v = orthogonalvect(normal, None)
  191.     base = quarkx.matrix(v, v^normal, -normal)
  192.     return base * m * (~base)
  193.  
  194. def squawk(text):
  195.   if quarkx.setupsubset(SS_MAP, "Options")["Developer"]:
  196.     quarkx.msgbox(text, MT_INFORMATION, MB_OK)
  197.  
  198. def findlabelled(list,label):
  199.   for item in list:
  200.     try:
  201.       if item.label==label:
  202.         return item
  203.     except (AttributeError):
  204.       pass
  205.  
  206. def cyclenext(i, len):
  207.     return (i+1)%len
  208.     
  209. def cycleprev(i, len):
  210.     return (i-1)%len
  211.  
  212. def projectpointtoplane(p,n1,o2,n2):
  213.   "project point to plane at o2 with normal n2 along normal n1"
  214.   v1 = o2-p
  215.   v2 = v1*n2
  216.   v3 = n1*n2
  217.   v4 = v2/v3
  218.   v5 = v4*n1
  219.   return p + v5
  220.  
  221. def matrix_u_v(u,v):
  222.     return quarkx.matrix((u.x, v.x, 0),
  223.                          (u.y, v.y, 0),
  224.                          (u.z, v.z, 1))
  225.                          
  226. def intersectionPoint2d(p0, d0, p1, d1):
  227.     "intersection in 2D plane, point, direction"
  228.     for v in p0, d0, p1, d1:
  229.         if v.z != 0.0:
  230.             return None
  231.     det = d0.x*d1.y-d1.x*d0.y
  232.     if det==0.0:
  233.         return 0  # lines paralell
  234.     s = (p0.y*d1.x - p1.y*d1.x - d1.y*p0.x +d1.y*p1.x)/det
  235.     return p0+s*d0
  236.         
  237. #
  238. # The two monstrosities below were derived from solving
  239. #   systems of equations in Maple V.  The idea is to think
  240. #   of the texture plane as being a plane in a 3d texture
  241. #   space, so the texture space<->map space mappings are
  242. #   invertible.  For the cases we're intererested in, the
  243. #   third texture coordinate is always zero, and drops out
  244. #   of the equations.
  245. #
  246. # Gets the s, t (texture) coordinates of a space point
  247. #  from the threepoints info and the point location(v)
  248. #
  249. def texCoords(v, texp, coeff=1):
  250.     p0 = texp[0]
  251.     d1 = texp[1]-p0
  252.     d2 = texp[2]-p0
  253.     c = d1^d2
  254.     denom = d1.x*c.z*d2.y-d1.x*d2.z*c.y-c.x*d1.z*d2.y-d2.x*d1.y*c.z+d1.z*d2.x*c.y+c.x*d1.y*d2.z
  255.     s = (-c.z*d2.y*p0.x+c.z*p0.y*d2.x-c.z*d2.x*v.y+c.z*v.x*d2.y+d2.y*c.x*p0.z-d2.y*c.x*v.z-c.y*p0.z*d2.x+c.y*v.z*d2.x+c.y*d2.z*p0.x-c.y*d2.z*v.x+v.y*c.x*d2.z-p0.y*c.x*d2.z)/denom
  256.     t = -(-d1.x*c.y*p0.z+d1.x*c.y*v.z+d1.x*c.z*p0.y-d1.x*c.z*v.y-d1.z*c.x*p0.y+d1.z*c.x*v.y+c.x*p0.z*d1.y-c.x*v.z*d1.y-p0.x*c.z*d1.y+p0.x*c.y*d1.z+v.x*c.z*d1.y-v.x*c.y*d1.z)/denom
  257.     return s*coeff, t*coeff
  258.  
  259. def solveForThreepoints((v1, (s1, t1)), (v2, (s2, t2)), (v3, (s3, t3))):
  260.     denom = s1*t2-s1*t3-t1*s2+t1*s3-s3*t2+t3*s2
  261.     p0x = -t2*v1.x*s3+v2.x*t1*s3-t3*s1*v2.x+t3*v1.x*s2+t2*s1*v3.x-v3.x*t1*s2
  262.     p0y = -t2*v1.y*s3+v2.y*t1*s3-t3*s1*v2.y+t3*v1.y*s2+t2*s1*v3.y-v3.y*t1*s2
  263.     p0z = -(t2*v1.z*s3-v2.z*t1*s3+t3*s1*v2.z-t3*v1.z*s2-t2*s1*v3.z+v3.z*t1*s2)
  264.     p0 = quarkx.vect(p0x, p0y, p0z)/denom
  265.     d1x = -(t2*v3.x-t2*v1.x+t3*v1.x-v3.x*t1+v2.x*t1-v2.x*t3)
  266.     d1y = -(t2*v3.y-t2*v1.y+t3*v1.y-v3.y*t1+v2.y*t1-v2.y*t3)
  267.     d1z = -(t2*v3.z-t2*v1.z+t3*v1.z-v3.z*t1+v2.z*t1-v2.z*t3)
  268.     d1 = quarkx.vect(d1x, d1y, d1z)/denom
  269.     d2x = -s1*v3.x+s1*v2.x-s3*v2.x+v3.x*s2-v1.x*s2+v1.x*s3
  270.     d2y = -s1*v3.y+s1*v2.y-s3*v2.y+v3.y*s2-v1.y*s2+v1.y*s3
  271.     d2z = -s1*v3.z+s1*v2.z-s3*v2.z+v3.z*s2-v1.z*s2+v1.z*s3
  272.     d2 = quarkx.vect(d2x, d2y, d2z)/denom
  273.     return p0, d1+p0, d2+p0
  274.     
  275. #
  276. # matrix for rotation taking u onto v
  277. #
  278. def matrix_rot_u2v(u,v):
  279.     axis = u^v
  280.     if axis:
  281.       axis = axis.normalized
  282.       import qhandles
  283.       return qhandles.UserRotationMatrix(axis, v, u, 0)
  284.     else:
  285.       matrix = quarkx.matrix("1 0 0 0 1 0 0 0 1")
  286.       if v*u > 0:
  287.         return matrix
  288.       else:
  289.         return ~matrix
  290.  
  291. def read2vec(vals):
  292.     if vals is None:
  293.        return None, None
  294.     strings = vals.split()
  295.     if len(strings)<2:
  296.        return None, None
  297.     return eval(strings[0]), eval(strings[1])
  298.  
  299. def readNvec(vals):
  300.     if vals is None:
  301.        return None
  302.     strings = vals.split()
  303.     return tuple(map(lambda str:eval(str), strings))
  304.  
  305. #
  306. # This one should be zapped soon, but not quite yet.
  307. #
  308. def buildLinearMatrix(dup):
  309.     linear = dup["matrix"]
  310.     matrix = quarkx.matrix('1 0 0 0 1 0 0 0 1')
  311.     if linear is not None:
  312.         matrix = quarkx.matrix(linear)*matrix
  313.     scale = dup["scale"]
  314.     if scale is not None:
  315.         matrix = quarkx.matrix('%.2f 0 0 0 %.2f 0 0 0 %.2f'%scale)*matrix
  316.     angles = dup["angles"]
  317.     if type(angles)==type(""):
  318.         angles = angles.split()
  319.         angles = eval(angles[0]), eval(angles[1]), eval(angles[2])
  320.  
  321.     if angles is not None:
  322.         angles = map(lambda a:a*deg2rad, angles)
  323.         matrix = matrix_rot_y(angles[0])*matrix_rot_x(angles[1])*matrix_rot_z(angles[2])*matrix
  324.     return matrix
  325.  
  326. def CaulkTexture():
  327.     tex = quarkx.setupsubset()["DefaultTextureCaulk"]
  328.     if tex is not None:
  329.         return tex
  330.     else:
  331.         return quarkx.setupsubset()["DefaultTexture"]
  332.  
  333.  
  334. #
  335. # returns n points, lying on the warped circle inscribed
  336. #  in the quadrilateral defined by the four points
  337. #  (tangent at the midpoints of the edges)
  338. #
  339. def warpedCircleFrom4Points(n, points):
  340.     #
  341.     # get the corners
  342.     #
  343.     corners=[]
  344.     for i in range(4):
  345.         corner=points[i]
  346.         corners.append((corner, (points[i-1]-corner)/2, (points[(i+1)%4]-corner)/2))
  347.     #
  348.     # make angle & corner from angle (degrees)
  349.     #
  350.     def angle_corner(angle, corners=corners):
  351.         if angle<90:
  352.             return angle, corners[0]
  353.         elif angle<180:
  354.             return angle-90, corners[1]
  355.         elif angle<270:
  356.             return angle-180, corners[2]
  357.         else:
  358.             return angle-270, corners[3]
  359.     #
  360.     # get angles
  361.     #
  362.     angle_incr=360/n
  363.     circle=[corners[0][0]+corners[0][1]]
  364.     cum_angle=0.0
  365.     for i in range(1,n):
  366.         cum_angle=cum_angle+angle_incr
  367.         angle, corner = angle_corner(cum_angle)
  368.         #
  369.         # get point in quarter-circle resting in
  370.         #   the axes
  371.         #
  372.         point = quarkx.vect(1.0-math.sin(angle*deg2rad), 1.0-math.cos(angle*deg2rad), 0)
  373.         #
  374.         # get linear matrix for mapping
  375.         #
  376.         mat = matrix_u_v(corner[1], corner[2])
  377.         circle.append(corner[0]+mat*point)
  378.     return circle    
  379.     
  380. # ----------- REVISION HISTORY ------------
  381. #
  382. #
  383. #$Log: maputils.py,v $
  384. #Revision 1.22  2003/12/18 21:51:46  peter-b
  385. #Removed reliance on external string library from Python scripts (second try ;-)
  386. #
  387. #Revision 1.21  2001/10/22 10:24:32  tiglari
  388. #live pointer hunt, revise icon loading
  389. #
  390. #Revision 1.19  2001/06/09 22:35:12  tiglari
  391. #add warpec circle routine
  392. #
  393. #Revision 1.18  2001/05/13 01:07:58  tiglari
  394. #disable buildLinearMapping, add CaulkTexture()
  395. #
  396. #Revision 1.17  2001/05/12 18:59:56  tiglari
  397. #remove buildLinearMatrix
  398. #
  399. #Revision 1.16  2001/05/06 10:15:32  tiglari
  400. #readNvec function, for arbitrary lenth string to tuple
  401. #
  402. #Revision 1.15  2001/05/06 06:02:19  tiglari
  403. #Support angles Typ E in buildLinearMatrix (so that angles handle will work)
  404. #
  405. #Revision 1.14  2001/04/17 23:29:07  tiglari
  406. #texture-rescaling bug fix (fixedscale="1" added to objects when they're
  407. #dragged to the panel, removed when inserted to the map, blocks
  408. #rescaling of textures on insert-by-pressing-panel-button
  409. #
  410. #Revision 1.13  2001/04/05 22:31:12  tiglari
  411. #buildLinearMatrix now checks matrix not linear
  412. #
  413. #Revision 1.12  2001/03/29 09:27:58  tiglari
  414. #scale & rotate specifics for duplicators
  415. #
  416. #Revision 1.11  2001/03/20 07:58:40  tiglari
  417. #customizable hot key support
  418. #
  419. #Revision 1.10  2001/03/18 23:59:44  tiglari
  420. #experimental merge
  421. #
  422. #
  423. #Revision 1.9  2001/03/12 23:08:57  tiglari
  424. #read2vec for path dup enhancements
  425. #
  426. #Revision 1.8  2001/03/04 06:41:15  tiglari
  427. #arbitrary axis rot matrix-producer added
  428. #
  429. #Revision 1.7  2001/02/14 11:04:36  tiglari
  430. #shifted some texture positioning utils in from maptexpin
  431. #
  432. #Revision 1.6  2000/09/04 21:27:56  tiglari
  433. #added 2d line intersection finder, vectors->matrix utility
  434. #
  435. #Revision 1.5  2000/08/21 11:25:16  tiglari
  436. #added projectpointtoplane (from plugins.maptagside)
  437. #
  438. #Revision 1.4  2000/07/29 02:04:54  tiglari
  439. #added cyclenext, cycleprev
  440. #
  441. #Revision 1.3  2000/07/24 09:06:56  tiglari
  442. #findlabelled added for finding items on menus (for face/texture revamp)
  443. #
  444. #Revision 1.2  2000/06/02 16:00:22  alexander
  445. #added cvs headers
  446. #
  447. #
  448. #